package com.enfry.sshftp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
import java.util.Vector;

import org.apache.commons.net.ftp.FTPClient;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.SCPClient;
import ch.ethz.ssh2.Session;

public class AppMain {
	Vector<String> files = new Vector<String>();

	public List<AppMain.precord> precordlist = new ArrayList<AppMain.precord>();
	
	public static final String DEFAULT_UPLOAD_DIR = "upload";

	/**
	 * 获取某个路径下的所有文件
	@param outFileLists 获取的文件集合
	@param filePath 目录
	@param subFolderFlag 是否为文件夹
	@param ext 查找的后缀名
	@return
	* 如果有子文件则为true
	 */
	private boolean getFileList(Vector<String> outFileLists, String filePath,boolean subFolderFlag, String ext) {
		if (outFileLists == null) {
			outFileLists = new Vector<String>();
		}
		File file = new File(filePath);
		if (file.exists()) {
			List<File> files = Arrays.asList(file.listFiles());
			//按照文件名排序
			Collections.sort(files, new Comparator< File>() {  
			   @Override  
			   public int compare(File o1, File o2) {  
			    if (o1.isDirectory() && o2.isFile())  
			          return -1;  
			    if (o1.isFile() && o2.isDirectory())  
			          return 1;  
			    return o1.getName().compareTo(o2.getName());  
			   }  
			  }
			);
			
			if (subFolderFlag) {
				for (int i = 0; i < files.size(); i++) {
					if (files.get(i).isFile()) {
						if (files.get(i).getName().indexOf("." + ext) > -1)
							outFileLists.add(filePath + "\\"+ files.get(i).getName());
					} else if (files.get(i).isDirectory()) {
						getFileList(outFileLists,
								filePath + "\\" + files.get(i).getName(),
								subFolderFlag, ext);
					}
				}
			} else {
				for (int i = 0; i < files.size(); i++) {
					if (files.get(i).isFile()) {
						if (files.get(i).getName().indexOf("." + ext) > -1)
							outFileLists.add(filePath + "\\"+ files.get(i).getName());
					}
				}
			}
		} else {
			return false;
		}
		return true;
	}
	
	private int chooseProject(){
		int i = 1;
		System.out.println("*******************************");
		System.out.println("1:打包EN+");
		System.out.println("2:打包GATEWAY");
		System.out.println("*******************************");
		System.out.println("请输入对应的数字：");
		Scanner sc = new Scanner(System.in);
		i = sc.nextInt();
		while (true) {
			try {
				if(i==1){
					System.out.println("你选择了1:打包EN+");
					break;
				} else if(i==2){
					System.out.println("你选择了2:打包GATEWAY");
					break;
				}
				System.out.println("超出数字范围，请重新选择：");
			} catch (Exception er) {
				System.out.println("请输入数字，请重新选择：");
			}
		}
		return i;
	}
	
	private int chooseType(){
		int i = 1;
		System.out.println("*******************************");
		System.out.println("1:打包");
		System.out.println("2:上传增量包");
		System.out.println("*******************************");
		System.out.println("请输入对应的数字：");
		Scanner sc = new Scanner(System.in);
		i = sc.nextInt();
		while (true) {
			try {
				if(i==1){
					System.out.println("你选择了1:打包");
					break;
				} else if(i==2){
					System.out.println("你选择了2:上传增量包");
					break;
				}
				System.out.println("超出数字范围，请重新选择：");
			} catch (Exception er) {
				System.out.println("请输入数字，请重新选择：");
			}
		}
		return i;
	}

	/**
	 * 选择需要上传的服务器配置文件
	@return
	*
	 */
	private int chooseFIle() {
		getFileList(this.files, System.getProperty("user.dir"), false, "xml");
		int i = 1;
		System.out.println("*******************************");
		for (String string : this.files) {
			System.out.println("[" + i + "]"+ string.replace(System.getProperty("user.dir"), ""));
			i++;
		}
		System.out.println("*******************************");
		System.out.println("请输入配置文件对应的数字：");
		while (true) {
			try {
				Scanner sc = new Scanner(System.in);
				i = sc.nextInt();
				if ((i <= this.files.size()) && (i > 0)) {
					System.out.println("你选择了配置文件："
							+ (String) this.files.get(i - 1));
					break;
				}
				System.out.println("超出数字范围，请重新选择：");
			} catch (Exception er) {
				System.out.println("请输入数字，请重新选择：");
			}
		}
		return i;
	}

	/**
	 * 读取配置文件里面的元素
	@param element 元素
	@param nodename 节点名称
	@return
	*
	 */
	private String getElementValue(Element element, String nodename) {
		try {
			Element ele = element.element(nodename);
			if (ele == null) {
				ele = element.element(nodename.toLowerCase());
				if (ele == null) {
					ele = element.element(nodename.toUpperCase());
					if (ele == null) {
						return "";
					}
				}
			}

			if ((ele.getTextTrim() == null) || (ele.getTextTrim().equals(""))
					|| (ele.getTextTrim().equals("null"))) {
				return "";
			}
			return ele.getTextTrim();
		} catch (Exception ex) {
		}
		return "";
	}

	/**
	 * 创建ftp远程路径
	@param remote 远程路径
	@param ftpClient ftp链接cliet对象
	@return 是否创建成功
	@throws IOException
	*
	 */
	private boolean CreateDirecroty(String remote, FTPClient ftpClient)
			throws IOException {
		String directory = remote.substring(0, remote.lastIndexOf("/") + 1);
		if ((!directory.equalsIgnoreCase("/"))
				&& (!ftpClient.changeWorkingDirectory(new String(directory
						.getBytes("GBK"), "iso-8859-1")))) {
			int start = 0;
			int end = 0;
			if (directory.startsWith("/"))
				start = 1;
			else {
				start = 0;
			}
			end = directory.indexOf("/", start);
			do {
				String subDirectory = new String(remote.substring(start, end)
						.getBytes("GBK"), "iso-8859-1");
				if (!ftpClient.changeWorkingDirectory(subDirectory)) {
					if (ftpClient.makeDirectory(subDirectory)) {
						ftpClient.changeWorkingDirectory(subDirectory);
					} else {
						System.out.println("创建目录失败");
						return false;
					}
				}

				start = end + 1;
				end = directory.indexOf("/", start);
			}

			while (end > start);
		}

		return true;
	}

	/**
	 * 上传方法
	@param uploadfilePath 默认为当前目录下的upload
	*
	 */
	private void upload(String uploadfilePath) {
		this.files.clear();
		if(uploadfilePath==null||"".equals(uploadfilePath)){
			uploadfilePath = DEFAULT_UPLOAD_DIR;
		}
		
		getFileList(this.files, System.getProperty("user.dir") + "\\"+uploadfilePath, true, "");
		for (int re = 0; re < this.precordlist.size(); re++) {
			System.out.println("开始传输文件："+ ((AppMain.precord) this.precordlist.get(re)).getName());
			try {
				String type = ((AppMain.precord) this.precordlist.get(re)).getType();
				String ip = ((AppMain.precord) this.precordlist.get(re)).getIp();
				String port = ((AppMain.precord) this.precordlist.get(re)).getPort();
				String path = ((AppMain.precord) this.precordlist.get(re)).getPath();
				String user = ((AppMain.precord) this.precordlist.get(re)).getUser();
				String pass = ((AppMain.precord) this.precordlist.get(re)).getPass();
				
				if (type.equals("ftp")) {
					ftpUpload(ip, port, path, user, pass,uploadfilePath);
				}else if (type.equals("ssh")) {
					sshUpload(ip, port, path, user, pass,uploadfilePath);
				}
				System.out.println("传输文件完成："+ ((AppMain.precord) this.precordlist.get(re)).getName());
			} catch (Exception Er) {
				System.out.println("传输文件异常："+ ((AppMain.precord) this.precordlist.get(re))
								.getName() + Er.getMessage());
			}
		}
		
		//上传之后，进行删除
		deleteFile();
	}

	/**
	 * 根据输入来判断  是否删除上传OK的本地文件
	@param local
	*
	 */
	private void deleteFile() {
		System.out.println("上传成功了，是否要删除文件(y=要删除；n=不删除)：");
		try {
			Scanner sc = new Scanner(System.in);
			String tex = sc.next();
			if(tex!=null&&tex.length()>0&&"y".equalsIgnoreCase(tex)){
				System.out.println("======你选择了删除文件=========");
				for (String fi : this.files) {
					File file = new File(fi);
					if(file.exists()&&file.delete()){
						System.out.println(fi+" 文件删除成功...");
					}
				}
			}else{
				System.out.println("======你选择了不删除文件=========");
			}
		} catch (Exception er) {
			er.printStackTrace();
			System.out.println("删除文件失败.."+er.getMessage());
		}
	}
	
	/**
	 * ssh上传批量文件
	@param ip
	@param port
	@param path
	@param user
	@param pass
	@throws NumberFormatException
	@throws IOException
	*
	 */
	private void sshUpload(String ip, String port, String path, String user,
			String pass,String uploadfilePath) throws NumberFormatException, IOException {
		Connection conn = new Connection(ip,Integer.parseInt(port));
		conn.connect();
		if(pass.contains("file:")){ //支持密钥的方式
			//输入密钥所在路径
			File keyfile = new File(pass.replaceAll("file:", "")); 
	 		//输入密钥的加密密码，没有可以设为 null
			String keyfilePass = null;
			
			boolean isAuthenticated = conn.authenticateWithPublicKey(user,keyfile,keyfilePass);
			if(!isAuthenticated){
				throw new IOException("Authentication failed.");
			}
		}else{ //支持用户密码链接方式
			conn.authenticateWithPassword(user, pass);
		}
		SCPClient client = new SCPClient(conn);
		for (String fi : this.files) {
			sshOneUpload(path, conn, client, fi,uploadfilePath);
		}
	}

	/**
	 * ssh上传单个文件
	@param path
	@param conn
	@param client
	@param fi
	*
	 */
	private boolean sshOneUpload(String path, Connection conn, SCPClient client,
			String fi,String uploadfileDir) {
		boolean isUpOK = false;
		try {
			String local = fi;
			
			String subStr = "\\"+uploadfileDir+"\\";
			int length = subStr.length();
			
			String remote = fi.substring(fi.indexOf(subStr) + length).replaceAll("\\\\", "/");
			String remotedic = "";
			if (remote.contains("/")) {
				remotedic = remote.substring(0,remote.lastIndexOf("/"));
				Session session = conn.openSession();
				session.execCommand("mkdir -p " + path + "/"+ remotedic);
				session.close();
			}
			client.put(local, path + "/" + remotedic);
			System.out.println("传输文件成功：" + remote);

			isUpOK = true;
		} catch (Exception ef) {
			ef.printStackTrace();
			System.out.println("传输文件失败：" + fi+" 原因："+ef.getMessage());
		}finally{
			if(!isUpOK){
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
					System.out.println("重新传输文件失败：" + fi+" 原因："+e.getMessage());
				}
				sshOneUpload(path,conn,client,fi,uploadfileDir);
			}
		}
		return isUpOK;
	}

	/**
	 * ftp上传
	@param ip
	@param port
	@param path
	@param user
	@param pass
	@throws SocketException
	@throws IOException
	@throws NumberFormatException
	*
	 */
	private void ftpUpload(String ip, String port, String path, String user,
			String pass,String uploadfileDir) throws SocketException, IOException,
			NumberFormatException {
		FTPClient ftpClient = new FTPClient();

		ftpClient.connect(ip, Integer.parseInt(port));
		ftpClient.setControlEncoding("UTF-8");
		ftpClient.login(user, pass);
		ftpClient.enterLocalPassiveMode();
		for (String fi : this.files) {
			ftpOneUpload(path, ftpClient, fi,uploadfileDir);
		}
	}

	/**
	 * ftp单个上传
	@param path
	@param ftpClient
	@param fi
	*
	 */
	private void ftpOneUpload(String path, FTPClient ftpClient, String fi,String uploadfileDir) {
		boolean isUpOk = false;
		try {
			ftpClient.changeWorkingDirectory(path);
			
			String subStr = "\\"+uploadfileDir+"\\";
			int length = subStr.length();
			
			String remote = fi.substring(fi.indexOf(subStr) + length).replaceAll("\\\\", "/");
			String remoteFileName = remote;
			if (remote.contains("/")) {
				remoteFileName = remote.substring(remote.lastIndexOf("/") + 1);
				CreateDirecroty(remote, ftpClient);
			}
			FileInputStream in = new FileInputStream(new File(fi));
			ftpClient.storeFile(remoteFileName, in);
			System.out.println("传输文件成功：" + remote);

			isUpOk = true;
		} catch (Exception ef) {
			ef.printStackTrace();
			System.out.println("传输文件失败：" + fi+" 原因："+ef.getMessage());
		}finally{
			if(!isUpOk){
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
					System.out.println("重新传输文件失败：" + fi+" 原因："+e.getMessage());
				}
				
				ftpOneUpload(path,ftpClient,fi,uploadfileDir);
			}
		}
	}

	/**
	 * 读取配置文件
	@param fi
	*
	 */
	private String readXml(int fi) {
		SAXReader reader = new SAXReader();
		String dir = "";
		try {
			Document document = reader.read(new File((String) this.files.get(fi - 1)));
			Element root = document.getRootElement();
			
			dir = getElementValue(root, "dir"); //读取配置的上传文件的路径名称。默认是当前路径下
			if(dir==null||"".equals(dir)){
				dir = DEFAULT_UPLOAD_DIR;
			}
			System.out.println("配置的dir上传路径是:"+dir);

			List list = root.elements("record");
			for (int re = 0; re < list.size(); re++) {
				Element params = (Element) list.get(re);
				AppMain.precord pre = new AppMain.precord();
				pre.setName(getElementValue(params, "name"));
				pre.setType(getElementValue(params, "type"));
				pre.setIp(getElementValue(params, "ip"));
				pre.setPort(getElementValue(params, "port"));
				pre.setUser(getElementValue(params, "user"));
				pre.setPass(getElementValue(params, "pass"));
				pre.setPath(getElementValue(params, "path"));

				this.precordlist.add(pre);
				System.out.println("[" + (re+1) + "]"+pre.getName());
			}
			
			System.out.println("[all]代表所有;(上传多个输入1#2 代表只传1和2两个tomcate)");
			while (true) {
				try {
					Scanner sc = new Scanner(System.in);
					String content = sc.next();
					if(content.toLowerCase().indexOf("all")>=0){
						break;
					}else{
						String[] split = content.split("#");
						List<AppMain.precord> tmp = new ArrayList<AppMain.precord>();
						for (int j = 0; j < split.length; j++) {
							int index = Integer.parseInt(split[j]);
							if(index<0||index>this.precordlist.size()){
								throw new Exception("输入错误~");
							}
							tmp.add(this.precordlist.get(index-1));
						}
						this.precordlist.clear();
						this.precordlist.addAll(tmp);
						break;
					}
				} catch (Exception er) {
					System.out.println(er.getMessage()+"请输入数字，请重新选择：");
				}
			}
			
			System.out.println("配置文件读取完成");
		} catch (Exception E) {
			System.out.print("配置文件读取失败：" + E.getMessage());
		}
		return dir;
	}

	public static void main(String[] args) {
		System.out.println("欢迎使用文件批量上传工具");
		AppMain ap = new AppMain();
		int chooseType = ap.chooseType();
		if(chooseType==2){
			int fi = ap.chooseFIle();
			String dir = ap.readXml(fi);
			ap.upload(dir);
		}else{
			int chooseProject = ap.chooseProject();
			ap.packageWar(chooseProject);
		}
	}
	
	private static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            //递归删除目录中的子目录下
            for (int i=0; i<children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空，可以删除
        return dir.delete();
    }
	
	private void run_cmd(String strcmd) {
        Runtime rt = Runtime.getRuntime(); //Runtime.getRuntime()返回当前应用程序的Runtime对象
        Process ps = null;  //Process可以控制该子进程的执行或获取该子进程的信息。
        try {
            ps = rt.exec(strcmd);   //该对象的exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序，并返回与该子进程对应的Process对象实例。
            ps.waitFor();  //等待子进程完成再往下执行。
        } catch (IOException e1) {
            e1.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        int i = ps.exitValue();  //接收执行完毕的返回值
        if (i == 0) {
            System.out.println("执行完成.");
        } else {
            System.out.println("执行失败.");
        }

        ps.destroy();  //销毁子进程
        ps = null;   
    }
	
	private void packageWar(int chooseProject){
		String uploadfilePath = null;
		String cmd = "";
		if(chooseProject==1){
			uploadfilePath = "upload_enfry";
			cmd = "package.bat";
		}else {
			uploadfilePath = "upload_gateway";
			cmd = "package_gateway.bat";
		}
		getFileList(this.files, System.getProperty("user.dir") + "\\"+uploadfilePath, true, "");
		File file1= new File(System.getProperty("user.dir") + "\\"+uploadfilePath+"_war");
		if(file1.exists()){
			deleteDir(file1);
		}
		for (String string : files) {
			String fileName = string.substring(string.lastIndexOf("\\"));
			String targetFilePath = string.substring(0, string.lastIndexOf("\\")).replace(uploadfilePath, uploadfilePath+"_war");
			File file= new File(targetFilePath);
			if(!file.exists()){
				file.mkdirs();
			}
			copyFile1(string, targetFilePath+fileName);
		}
		
		run_cmd("cmd /c \""+System.getProperty("user.dir") + "\\"+cmd+"\"");
	}
	
	public void copyFile1(String oldPath, String newPath) { 
		try { 
			int bytesum = 0; 
			int byteread = 0; 
			File oldfile = new File(oldPath); 
			if (oldfile.exists()) { //文件存在时 
				InputStream inStream = new FileInputStream(oldPath); //读入原文件 
				FileOutputStream fs = new FileOutputStream(newPath); 
				byte[] buffer = new byte[1444]; 
				int length; 
				while ( (byteread = inStream.read(buffer)) != -1) { 
				bytesum += byteread; //字节数 文件大小 
				System.out.println(bytesum); 
				fs.write(buffer, 0, byteread); 
				} 
				inStream.close(); 
				fs.close();
			} 
		} catch (Exception e) { 
			System.out.println("复制单个文件操作出错"); 
			e.printStackTrace(); 
		} 
	}

	/**
	 * 内部类，配置子类
	* @ClassName: precord 
	* @author zhoushiguo@wegooooo
	* @date 2015年11月24日 下午5:28:43
	 */
	public class precord {
		private String type = "";
		private String ip = "";
		private String port = "";
		private String user = "";
		private String pass = "";
		private String name = "";
		private String path = "";

		public String getPath() {
			return this.path;
		}

		public void setPath(String path) {
			this.path = path;
		}

		public String getName() {
			return this.name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public precord() {
		}

		public String getType() {
			return this.type;
		}

		public void setType(String type) {
			this.type = type;
		}

		public String getIp() {
			return this.ip;
		}

		public void setIp(String ip) {
			this.ip = ip;
		}

		public String getPort() {
			return this.port;
		}

		public void setPort(String port) {
			this.port = port;
		}

		public String getUser() {
			return this.user;
		}

		public void setUser(String user) {
			this.user = user;
		}

		public String getPass() {
			return this.pass;
		}

		public void setPass(String pass) {
			this.pass = pass;
		}
	}
}